import 'dart:math';

import 'package:htxd_flutter_mapbox_sdk/entity/point_entity.dart';
import 'package:polybool/polybool.dart' as polybool;
import 'package:turf/src/rhumb_destination.dart';
import 'package:turf/turf.dart';

const double EARTHRADIUS = 6378137.0;

const double DEG_TO_RAD = 0.0174532925199432958;

/// 空间计算
class TurfGeometryUtil {
  /// 计算多边形的长度
  static double calculateLength(List<Position> points) {
    double result = 0.0;
    if (points.length < 2) {
      return result;
    }
    for (var i = 0; i < points.length - 1; i++) {
      result += distance(Point(coordinates: points[i]),
          Point(coordinates: points[i + 1]), Unit.meters);
    }
    return result;
  }

  /// 根据提供的多边形顶点坐标[points]计算多边形的面积，平方米
  static double calculatePointArea(List<PointEntity> points) {
    List<Position> positions = [];
    for (var value in points) {
      positions.add(Position(value.longitude as num, value.latitude as num));
    }
    return calculateArea(positions);
  }

  /// 根据提供的多边形顶点坐标[points]计算多边形的面积，平方米
  static double calculateArea(List<Position> points) {
    int triangleCount = 3;
    if (points.length < triangleCount) {
      return 0.0;
    }

    double s = 0;
    double metrePerDegree = EARTHRADIUS * DEG_TO_RAD;
    int count = points.length;
    for (int i = 0; i < count; ++i) {
      Position coordPrev = points[i];
      Position coordNext = points[((i + 1) % count)];

      double x1 =
          coordPrev.lng * metrePerDegree * cos(coordPrev.lat * DEG_TO_RAD);
      double y1 = coordPrev.lat * metrePerDegree;
      double x2 =
          coordNext.lng * metrePerDegree * cos(coordNext.lat * DEG_TO_RAD);
      double y2 = coordNext.lat * metrePerDegree;

      s += x1 * y2 - x2 * y1;
    }
    return (s / 2.0).abs();
  }

  /// 计算距离
  ///用haversine公式计算经纬度两点间的距离,
  ///注意：这里将地球当做了一个正球体来计算距离，当经纬度跨度较大时，有轻微的距离误差
  static double distanceBetween(Position latLng1, Position latLng2) {
    //经纬度转换成弧度
    double lat1 = _convertDegreesToRadians(latLng1.lat.toDouble());
    double lon1 = _convertDegreesToRadians(latLng1.lng.toDouble());
    double lat2 = _convertDegreesToRadians(latLng2.lat.toDouble());
    double lon2 = _convertDegreesToRadians(latLng2.lng.toDouble());
    //差值
    double detalLat = (lat1 - lat2).abs();
    double detalLon = (lon1 - lon2).abs();
    double h =
        _haverSin(detalLat) + cos(lat1) * cos(lat2) * _haverSin(detalLon);
    return (2 * EARTHRADIUS * asin(sqrt(h)));
  }

  static double _haverSin(double theta) {
    var v = sin(theta / 2);
    return v * v;
  }

  /// 将角度换算为弧度。
  static double _convertDegreesToRadians(double degrees) {
    return degrees * pi / 180;
  }

  /// 根据目标点获取距离一定距离的点,distance 的单位是米
  static List<Position> getCircle(Position target, double distance) {
    List<Position> positions = [];
    for (int i = 0; i < 64; i++) {
      positions.add(calculateRhumbDestination(target, distance, i * 360 / 64));
    }
    positions.add(positions[0]);
    return positions;
  }

  /// 根据地块重叠，获取重叠部分
  static List<List<Position>> getOverlapsPolygon(List<PointEntity> points1,
      List<PointEntity> points2) {
    List<polybool.Coordinate> coord1 = [];
    for (var value in points1) {
      coord1.add(polybool.Coordinate(value.longitude!, value.latitude!));
    }
    polybool.Polygon polygon1 = polybool.Polygon(regions: [coord1]);
    List<polybool.Coordinate> coord2 = [];
    for (var value in points2) {
      coord2.add(polybool.Coordinate(value.longitude!, value.latitude!));
    }
    polybool.Polygon polygon2 = polybool.Polygon(regions: [coord2]);
    var intersect = polygon1.intersect(polygon2);
    var regions2 = intersect.regions;
    List<List<Position>> result = [];
    for (var value1 in regions2) {
      if (value1.isNotEmpty) {
        List<Position> points = [];
        for (var value in value1) {
          points.add(Position(value.x, value.y));
        }
        if (points.isNotEmpty) {
          points.add(points[0]);
          result.add(points);
        }
      }
    }
    return result;
  }

  /// 图形是否是简单图形
  static bool isSimple(List<PointEntity> points) {
    int n = points.length;
    for (int i = 0; i < n - 2; i++) {
      Line edge1 = Line(points[i], points[(i + 1) % n]);
      for (int j = i + 2; j < n - 1; j++) {
        if (i == 0 && j + 1 == n - 1) {
          continue;
        }
        Line edge2 = Line(points[j], points[(j + 1) % n]);
        if (_doLinesIntersect(edge1, edge2)) {
          return false;
        }
      }
    }
    return true;
  }
}

class Line {
  PointEntity p1;
  PointEntity p2;

  Line(this.p1, this.p2);
}

bool _doLinesIntersect(Line line1, Line line2) {
  double x1 = line1.p1.longitude!;
  double y1 = line1.p1.latitude!;
  double x2 = line1.p2.longitude!;
  double y2 = line1.p2.latitude!;
  double x3 = line2.p1.longitude!;
  double y3 = line2.p1.latitude!;
  double x4 = line2.p2.longitude!;
  double y4 = line2.p2.latitude!;

  double d1 = _direction(x3, y3, x4, y4, x1, y1);
  double d2 = _direction(x3, y3, x4, y4, x2, y2);
  double d3 = _direction(x1, y1, x2, y2, x3, y3);
  double d4 = _direction(x1, y1, x2, y2, x4, y4);

  if (((d1 > 0 && d2 < 0) || (d1 < 0 && d2 > 0)) &&
      ((d3 > 0 && d4 < 0) || (d3 < 0 && d4 > 0))) {
    return true;
  } else if (d1 == 0 && _isOnSegment(x3, y3, x4, y4, x1, y1)) {
    return true;
  } else if (d2 == 0 && _isOnSegment(x3, y3, x4, y4, x2, y2)) {
    return true;
  } else if (d3 == 0 && _isOnSegment(x1, y1, x2, y2, x3, y3)) {
    return true;
  } else if (d4 == 0 && _isOnSegment(x1, y1, x2, y2, x4, y4)) {
    return true;
  }

  return false;
}

double _direction(double x1, double y1, double x2, double y2, double x,
    double y) {
  return (x2 - x1) * (y - y1) - (x - x1) * (y2 - y1);
}

bool _isOnSegment(double x1, double y1, double x2, double y2, double x,
    double y) {
  return (x >= x1 && x <= x2 && y >= y1 && y <= y2) ||
      (x >= x2 && x <= x1 && y >= y2 && y <= y1);
}